package net.guruqu.cds;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Random;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;

import com.sun.org.apache.bcel.internal.generic.GETSTATIC;

public class CDSNode extends Node {

	int round;
	HashMap<IntPair, Vector<Integer>> coverEquivalent;
	HashMap<Integer, Boolean> surviveList;
	boolean chosen;
	boolean converge;
	float chosenProbability;
	static Random random;

	static {
		random = new Random(137);
	}

	public CDSNode() {
		round = 0;
		chosen = false;
		coverEquivalent = new HashMap<IntPair, Vector<Integer>>();
		surviveList = new HashMap<Integer, Boolean>();
		chosenProbability = 0.5f;
	}

	private static Collection<Integer> findCommon(Collection<Integer> la,
			Collection<Integer> lb) {
		HashSet<Integer> sa = new HashSet<Integer>();
		HashSet<Integer> sb = new HashSet<Integer>();
		sa.addAll(la);
		sb.addAll(lb);

		HashSet<Integer> common = new HashSet<Integer>();
		for (Integer i : sa) {
			if (sb.contains(i))
				common.add(i);
		}
		return common;
	}

	private void announceChosenDecision() {
		HashSet<Integer> toSet = new HashSet<Integer>();
		for (IntPair i : coverEquivalent.keySet()) {
			toSet.addAll(coverEquivalent.get(i));
		}
		for (Integer i : toSet) {
			sendMessage(i, isChosen());
		}
	}

	@Override
	public void executeNode() {
		HashMap<Integer, LinkedList<Object>> incoming = getIncomingMessage();
		if (round == 0) {
			// Round 0 , every node send its neighbour information to all its
			// neighbour
			Vector<Integer> neighbour = getNeighbour();
			for (Integer i : neighbour) {
				sendMessage(i, neighbour);
			}
			for (int i = 0; i < neighbour.size(); i++) {
				// surviveList.put(neighbour.get(i), Boolean.FALSE);
				for (int j = i + 1; j < neighbour.size(); j++) {
					coverEquivalent.put(new IntPair(neighbour.get(i), neighbour
							.get(j)), new Vector<Integer>());

				}
			}
		}

		if (round == 1) {
			LinkedList<Object> removeList = new LinkedList<Object>();
			for (IntPair i : coverEquivalent.keySet()) {
				Collection<Integer> na = (Collection) incoming.get(i.value[0])
						.getFirst();
				Collection<Integer> nb = (Collection) incoming.get(i.value[1])
						.getFirst();

				Collection<Integer> common = findCommon(na, nb);
				if (nb.contains(i.value[0]) && na.contains(i.value[1]))
					removeList.add(i);

				common.remove((Object) getUID());
				coverEquivalent.get(i).addAll(common);
				// System.out.print(getUID() + "=");
				// System.out.println(common);
			}
			for (Object rm : removeList) {
				coverEquivalent.remove(rm);
			}
			clearIncomingMessage();

			announceChosenDecision();
			// broadCastMessage(Boolean.FALSE);
		}
		if (round > 1) {
			// Random choosing whether or not it survives
			for (Integer i : incoming.keySet()) {
				if (incoming.get(i).size() > 0) {
					Boolean bool = (Boolean) incoming.get(i).getFirst();
					surviveList.remove(i);
					surviveList.put(i, bool);
				}
			}
			boolean allCovered = true;
			for (IntPair i : coverEquivalent.keySet()) {
				// i is the pairs that this node is covering
				boolean covered = false;
				for (Integer j : coverEquivalent.get(i)) {
					// j is the node that also cover pair i
					if (surviveList.get(j)) {
						// one of the other node is covering pair i
						covered = true;
						break;
					}
				}
				if (!covered) {
					// pair i is not covered by any node
					allCovered = false;
					break;
				}
			}
			if (!chosen) {
				if (!allCovered) {
					if (random.nextFloat() < chosenProbability) {
						setChosen(true);
					}
					converge = false;
				} else {
					converge = true;
				}
			} else {
				if (allCovered) {
					if (random.nextFloat() < 1.0f - chosenProbability) {
						setChosen(false);
					}
					converge = false;
				} else {
					converge = true;
				}
			}
			clearIncomingMessage();
			// broadCastMessage(isChosen());
			announceChosenDecision();
			// System.out.printf("%d=%s\n", getUID(), isChosen());
			// System.out.printf("%s\n",surviveList.toString());
		}
		round++;
	}

	public static class IntPair {
		public Integer value[];

		public IntPair(int a, int b) {
			value = new Integer[] { a, b };
		}

		public String toString() {
			return "{" + value[0] + "," + value[1] + "}";
		}

		public int hashCode() {
			return value[0].hashCode() * value[1].hashCode();
		}

		public boolean equals(Object obj) {
			return ((IntPair) obj).value[0] == value[0]
					&& ((IntPair) obj).value[1] == value[1];
		}
	}

	public static void main(String s[]) {
		// HashMap<IntPair,Object> o = new HashMap<IntPair, Object>();
		// o.put(new IntPair(1,2), new Object());
		// System.out.println(o.get(new IntPair(1,2)));
	}

	public boolean isChosen() {
		return chosen;
	}

	public void setChosen(boolean chosen) {
		this.chosen = chosen;
	}

	public boolean isConverge() {
		return converge;
	}

	public void setConverge(boolean converge) {
		this.converge = converge;
	}
}
